/**
 * Created by IntelliJ IDEA.
 * User: martlenn
 * Date: 24-Jan-2008
 * Time: 05:15:48
 */
package org.hkupp.util;

import java.util.ArrayList;
/*
 * CVS information:
 *
 * $Revision$
 * $Date$
 */

/**
 * This class splits a String into parts based on the separator it is configured with.
 * The major difference with a StringTokenizer (to which it is otherwise very comparable)
 * is that it parses two consecutive separators as 'separator', 'empty String', and separator
 * (StringTokenizer takes this to be 'separator').
 *
 * @author Lennart Martens
 * @version $Id$
 */
public class SeparatedStringSplitter {

    private String iString = null;
    private String iSeparator = null;
    private boolean iAutoTrim = false;

    private int index = 0;
    private Offsets[] iOffsets = null;


    public SeparatedStringSplitter(String aString, String aSeparator) {
        this(aString, aSeparator, false);
    }

    public SeparatedStringSplitter(String aString, String aSeparator, boolean aAutoTrim) {
        iString = aString;
        iSeparator = aSeparator;
        iAutoTrim = aAutoTrim;
    }

    public String[] allTokens() {
        if(iOffsets == null) {
            calculateOffsets();
        }

        String[] result = new String[iOffsets.length];
        for (int i = 0; i < iOffsets.length; i++) {
            Offsets offset = iOffsets[i];
            result[i] = iString.substring(offset.getStart(), offset.getEnd());
            if(iAutoTrim) {
                result[i] = result[i].trim();
            }
        }
        return result;
    }

    /**
     * Zero-based index token retrieval.
     *
     * @param aIndex    int with the index of the token to retrieve.
     *                  '0'is the first token!
     * @return  String with the corresponding token.
     */
    public String getToken(int aIndex) {
        if(iOffsets == null) {
            calculateOffsets();
        }
        if(aIndex >= iOffsets.length) {
            throw new ArrayIndexOutOfBoundsException("You requested token nbr. " + aIndex + " for this splitter, but there are only " + (iOffsets.length-1) + " tokens available!");
        }
        if(aIndex < 0) {
            throw new IllegalArgumentException("Token with negative index (" + aIndex + ") requested! Use zero-based nonnegative integers instead!");
        }

        String result = iString.substring(iOffsets[aIndex].getStart(), iOffsets[aIndex].getEnd());

        if(iAutoTrim) {
            result = result.trim();
        }

        return result;
    }

    public String nextToken() {
        if(iOffsets == null) {
            calculateOffsets();
        }
        if(index >= iOffsets.length) {
            throw new ArrayIndexOutOfBoundsException("All tokens for this splitter have already been requested! You can start from the top again by calling the 'resetIndex()' method.");
        }
        String result = iString.substring(iOffsets[index].getStart(), iOffsets[index].getEnd());
        index++;
        if(iAutoTrim) {
            result = result.trim();
        }
        return result;
    }

    public void resetIndex() {
        index = 0;
    }

    public int countTokens() {
        if(iOffsets == null) {
            calculateOffsets();
        }
        return iOffsets.length;
    }

    private void calculateOffsets() {
        ArrayList<Offsets> offsets = new ArrayList<Offsets>();
        int previousLocation = 0;
        int location = -1;
        while((location = iString.indexOf(iSeparator, previousLocation)) >= 0) {
            offsets.add(new Offsets(previousLocation, location));
            previousLocation = location + iSeparator.length();
        }
        // Fencepost.
        offsets.add(new Offsets(previousLocation, iString.length()));

        // Transform to array.
        iOffsets = new Offsets[offsets.size()];
        offsets.toArray(iOffsets);
    }

    private class Offsets {
        private int iStart = -1;
        private int iEnd = -1;

        public Offsets(int aStart, int aEnd) {
            this.iStart = aStart;
            this.iEnd = aEnd;
        }

        public int getEnd() {
            return iEnd;
        }

        public int getStart() {
            return iStart;
        }
    }
}
